
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanaintlib.machine;

// import managarm virtual architecture classes
import org.managarm.jmanaintlib.errors.VmNullError;
import org.managarm.jmanavalib.core.VaClass;
import org.managarm.jmanavalib.core.VaClsInfo;
import org.managarm.jmanavalib.core.VaDatatype;
import org.managarm.jmanavalib.core.VaFrame;
import org.managarm.jmanavalib.core.VaInst;
import org.managarm.jmanavalib.core.VaMthInfo;
import org.managarm.jmanavalib.core.VaProcedure;

public final class VmVirtualCode extends VmCode {
	private final class VirtualExecutor extends VmExecutor {
		private final VaProcedure procedure; // our procedure
		
		// current frame and current instruction pointer
		private VaFrame currentFrame;
		private int currentIp;
		private boolean returnStatus;
		private VmCell returnValue;
		
		private VmCell[] currentRegisters;	// current register set/stack	
		private int stackSize; // current register stack size
		
		private VirtualExecutor catchExecutor;
		
		public VirtualExecutor(VaProcedure procedure) {
			this.procedure = procedure;
			
			// initialize the first frame and push our arguments
			// onto the register stack
			initFrame(getFrame(0));
		}
		
		@Override public final void pushArgument(VmCell object) {
			pushRegister(object);
		}
		
		@Override public final VmCode getCodeObject() {
			return VmVirtualCode.this;
		}
		
		@Override public final boolean hasReturned() {
			return returnStatus;
		}
		
		@Override public final VmCell getReturn() {
			return returnValue;
		}
		
		@Override public final void processReturn(VmCell result)
				throws VmError {
			final VaInst opcode = currentFrame.getInst(currentIp);
			if(opcode instanceof VaInst.InstInvoke) {
				final VaInst.InstInvoke inst = (VaInst.InstInvoke)opcode;
				if(inst.result)
					pushRegister(result);
				
				currentIp++;
			}else{
				throw new VmError("Illegal virtual machine state");
			}
		}
		
		@Override public final boolean catchException(VmCell exception)
				throws VmError {
			if(catchExecutor == null)
				return false;
			
			final VaInst catch_opcode = currentFrame.getInst(currentIp);
			if(catch_opcode instanceof VaInst.InstCatch == false)
				throw new VmError("Illegal virtual machine state");
			
			final VaInst.InstCatch catch_inst = (VaInst.InstCatch)catch_opcode;
			
			// create the catch-frame
			initFrame(getFrame(catch_inst.frame));
			
			// setup the argument registers
			pushRegister(exception);
			for(int i = 0; i < catchExecutor.currentIp; i++) {
				final VaInst pass_opcode
						= catchExecutor.currentFrame.getInst(i);
				if(pass_opcode instanceof VaInst.InstPass == false)
					continue;
				
				final VaInst.InstPass pass_inst = (VaInst.InstPass)pass_opcode;
				setRegister(pass_inst.argument,
						catchExecutor.getRegister(pass_inst.register));
			}
			
			catchExecutor = null;
			return true;
		}
		
		@Override public final void singleStep(VmState vmstate)
				throws VmError {
			final VaInst opcode = currentFrame.getInst(currentIp);
			if(opcode instanceof VaInst.InstLoad) {
				final VaInst.InstLoad inst = (VaInst.InstLoad)opcode;
				pushRegister(VmUtils.createCell(inst.literal));
				currentIp++;
			}else if(opcode instanceof VaInst.InstReturn) {
				final VaInst.InstReturn inst = (VaInst.InstReturn)opcode;
				if(inst.register != 0)
					returnValue = getRegister(inst.register);
				returnStatus = true;
			}else if(opcode instanceof VaInst.InstBinOperation) {
				final VaInst.InstBinOperation inst
					= (VaInst.InstBinOperation)opcode;
				pushRegister(doBinOperation(inst.operator, inst.left, inst.right));
				currentIp++;
			}else if(opcode instanceof VaInst.InstUnOperation) {
				final VaInst.InstUnOperation inst
					= (VaInst.InstUnOperation)opcode;
				pushRegister(doUnOperation(inst.operator, inst.operand));
				currentIp++;
			}else if(opcode instanceof VaInst.InstJump) {
				final VaInst.InstJump inst = (VaInst.InstJump)opcode;
				if(inst.register == 0
						|| getRegister(inst.register).valueAsBoolean()) {
					final VmCell[] registers = currentRegisters;
					initFrame(getFrame(inst.frameId));
					// setup the register array
					for(int i = 0; i < inst.getArgumentCnt(); i++){
						pushRegister(registers[inst.getArgument(i) - 1]);
					}
				}else{
					currentIp++;
				}
			}else if(opcode instanceof VaInst.InstInvoke) {
				final VaInst.InstInvoke inst = (VaInst.InstInvoke)opcode;
				
				// get the object register
				VmCell object = null;
				if(inst.object != 0)
					object = getRegister(inst.object);
				
				// find the target procedure
				final VmCode target_code;
				if(object instanceof VmCellDelegate) {
					target_code = machine.getCode(object.delegateMethod());
				}else if(inst.virtual) {
					target_code = machine.findMethod(inst.mthInfo,
							object.getClsInfo());
				}else{
					target_code = machine.getCode(inst.mthInfo);
				}
				
				if(target_code == null && object instanceof VmCellDelegate) {
					throw new VmError("Could not lookup method "
							+ object.delegateMethod().clsInfo.ident
							+ ":" + object.delegateMethod().ident);
				}else if(target_code == null) {
					throw new VmError("Could not lookup method "
							+ inst.mthInfo.clsInfo.ident + ":"
							+ inst.mthInfo.ident);
				}
				
				// push all arguments
				final VmExecutor executor = target_code.invoke();
				for(int i = 0; i < inst.getArgumentCnt(); i++)
					executor.pushArgument(getRegister(inst.getArgument(i)));
				if(object instanceof VmCellDelegate
						&& (object.delegateMethod().flags0 & VaMthInfo.FLAG0_STATIC) == 0) {
					// non-static delegate call: push the delegate object
					executor.pushArgument(object.delegateObject());
				}else if(object instanceof VmCellObject
						&& (inst.mthInfo.flags0 & VaMthInfo.FLAG0_STATIC) == 0) {
					// non-static method call: push the object
					executor.pushArgument(object);
				}
				
				vmstate.pushMethod(executor); // push the method onto the stack
			}else if(opcode instanceof VaInst.InstCreateObject) {
				final VaInst.InstCreateObject inst
						= (VaInst.InstCreateObject)opcode;
				if(inst.datatype instanceof VaDatatype.DtClass) {
					final VaDatatype.DtClass type = (VaDatatype.DtClass)inst.datatype;
					pushRegister(new VmCellObject(machine, type.clsInfo, machine.objectId));
					machine.objectId++;
				}else{
					throw new VmError("Invalid data type for "
							+ opcode.getClass().getName());
				}
				currentIp++;
			}else if(opcode instanceof VaInst.InstCreateArray) {
				final VaInst.InstCreateArray inst
						= (VaInst.InstCreateArray)opcode;
				final VmCell size = getRegister(inst.size);
				pushRegister(createArray(inst.datatype, size.valueAsUInt()));
				currentIp++;
			}else if(opcode instanceof VaInst.InstArrAccess) {
				final VaInst.InstArrAccess inst
						= (VaInst.InstArrAccess)opcode;
				final VmCell array = getRegister(inst.array);
				final VmCell index = getRegister(inst.index);
				if(inst.operator == VaInst.InstArrAccess.OP_LOAD) {
					pushRegister(array.loadElement(index.valueAsUInt()));
				}else if(inst.operator == VaInst.InstArrAccess.OP_STORE) {
					array.storeElement(index.valueAsUInt(), getRegister(inst.operand));
				}
				currentIp++;
			}else if(opcode instanceof VaInst.InstFldAccess) {
				final VaInst.InstFldAccess inst
						= (VaInst.InstFldAccess)opcode;
				if(inst.operator == VaInst.InstFldAccess.OP_LOAD
						&& inst.object == 0) {
					pushRegister(machine.getStatic(inst.fldInfo));
				}else if(inst.operator == VaInst.InstFldAccess.OP_STORE
						&& inst.object == 0) {
					machine.putStatic(inst.fldInfo, getRegister(inst.operand));
				}else if(inst.operator == VaInst.InstFldAccess.OP_LOAD
						&& inst.object != 0) {
					final VmCell object = getRegister(inst.object);
					pushRegister(object.loadField(inst.fldInfo));
				}else if(inst.operator == VaInst.InstFldAccess.OP_STORE
						&& inst.object != 0) {
					final VmCell object = getRegister(inst.object);
					object.storeField(inst.fldInfo, getRegister(inst.operand));
				}else{
					throw new VmError("Invalid operator for "
							+ opcode.getClass().getName());
				}
				currentIp++;
			}else if(opcode instanceof VaInst.InstCast) {
				final VaInst.InstCast inst = (VaInst.InstCast)opcode;
				// FIXME: perform type checks
				pushRegister(getRegister(inst.source).castTo(inst.datatype));
				currentIp++;
			}else if(opcode instanceof VaInst.InstInstanceof) {
				final VaInst.InstInstanceof inst = (VaInst.InstInstanceof)opcode;
				if(inst.datatype instanceof VaDatatype.DtClass == false)
					throw new VmError("Invalid data type for "
							+ opcode.getClass().getName());
				
				final VaDatatype.DtClass datatype = (VaDatatype.DtClass)inst.datatype;
				
				if((datatype.clsInfo.flags0 & VaClsInfo.FLAG0_FINAL) != 0) {
					pushRegister(new VmCellBoolean(getRegister(inst.object).getClsInfo()
							.compatible(datatype.clsInfo)));
				}else{
					pushRegister(new VmCellBoolean(isSuperclass
							(getRegister(inst.object).getClsInfo(), datatype.clsInfo)));
				}
				currentIp++;
			}else if(opcode instanceof VaInst.InstCatch) {
				final VaInst.InstCatch inst = (VaInst.InstCatch)opcode;
				
				catchExecutor = new VirtualExecutor(inst.procedure);
				for(int i = 0; i < inst.getArgumentCnt(); i++)
					catchExecutor.pushArgument(getRegister(inst.getArgument(i)));
				vmstate.pushMethod(catchExecutor);
			}else if(opcode instanceof VaInst.InstThrow) {
				final VaInst.InstThrow inst = (VaInst.InstThrow)opcode;
				vmstate.throwException(getRegister(inst.exception));
			}else if(opcode instanceof VaInst.InstPass) {
				// ignore this instruction; it is handled by unwindException()
				currentIp++;
			}else if(opcode instanceof VaInst.InstLeave) {
				vmstate.throwException(new VmCellNull());
			}else if(opcode instanceof VaInst.InstDlgConstruct) {
				final VaInst.InstDlgConstruct inst
						= (VaInst.InstDlgConstruct)opcode;
				if((inst.method.flags0 & VaMthInfo.FLAG0_STATIC) != 0) {
					pushRegister(new VmCellDelegate(inst.method, null));
				}else{
					if(getRegister(inst.object) instanceof VmCellNull)
						throw new VmNullError();
					pushRegister(new VmCellDelegate(inst.method,
							getRegister(inst.object)));
				}
				currentIp++;
			}else if(opcode instanceof VaInst.InstXPerfArrayCopy) {
				final VaInst.InstXPerfArrayCopy inst =
					(VaInst.InstXPerfArrayCopy)opcode;
				final VmCell dest = getRegister(inst.dest);
				final VmCell source = getRegister(inst.source);
				dest.arrayCopy(source, getRegister(inst.destOffset).valueAsUInt(),
						getRegister(inst.sourceOffset).valueAsUInt(),
						getRegister(inst.size).valueAsUInt());
				currentIp++;
			}else{
				throw new VmError("Invalid instruction "
						+ opcode.getClass().getName());
			}
		}
		
		private final VmCell doBinOperation(int operator,
				int leftreg, int rightreg) throws VmError {
			final VmCell left = getRegister(leftreg);
			final VmCell right = getRegister(rightreg);

			final VmCell intermediate;
			if(operator == VaInst.InstBinOperation.OP_ARITH_ADD
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						+ right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_SUB
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						- right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_MUL
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						* right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_DIV
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						/ right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_MOD
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						% right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_OR
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						| right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_AND
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						& right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_XOR
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						^ right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_SHL
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						<< right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_SHR
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				intermediate = new VmCellUInt(left.valueAsUInt()
						>> right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				return new VmCellBoolean(left.valueAsUInt()
						== right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_ABOVE
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				return new VmCellBoolean(left.valueAsUInt()
						> right.valueAsUInt());
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_BELOW
					&& left instanceof VmCellUInt
					&& right instanceof VmCellUInt) {
				return new VmCellBoolean(left.valueAsUInt()
						< right.valueAsUInt());
			/* ----- CHARACTER COMPARISON ----- */
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellChar
					&& right instanceof VmCellChar) {
				return new VmCellBoolean(left.valueAsChar()
						== right.valueAsChar());
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_ABOVE
					&& left instanceof VmCellChar
					&& right instanceof VmCellChar) {
				return new VmCellBoolean(left.valueAsChar()
						> right.valueAsChar());
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_BELOW
					&& left instanceof VmCellChar
					&& right instanceof VmCellChar) {
				return new VmCellBoolean(left.valueAsChar()
						< right.valueAsChar());
			/* ----- BOOLEAN COMPARISON ----- */
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellBoolean
					&& right instanceof VmCellBoolean) {
				return new VmCellBoolean(left.valueAsBoolean()
						== right.valueAsBoolean());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_AND
					&& left instanceof VmCellBoolean
					&& right instanceof VmCellBoolean) {
				return new VmCellBoolean(left.valueAsBoolean()
						&& right.valueAsBoolean());
			}else if(operator == VaInst.InstBinOperation.OP_ARITH_OR
					&& left instanceof VmCellBoolean
					&& right instanceof VmCellBoolean) {
				return new VmCellBoolean(left.valueAsBoolean()
						|| right.valueAsBoolean());
			/* ----- NULL COMPARISON ----- */
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellNull
					&& right instanceof VmCellNull) {
				return new VmCellBoolean(true);
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellNull
					&& (right instanceof VmCellArray
							|| right instanceof VmCellObject
							|| right instanceof VmCellArrUByte)) {
				return new VmCellBoolean(false);
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& (left instanceof VmCellArray
							|| left instanceof VmCellObject
							|| left instanceof VmCellArrUByte)
					&& right instanceof VmCellNull) {
				return new VmCellBoolean(false);
			/* ----- OBJECT COMPARISON ----- */
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& left instanceof VmCellObject
					&& right instanceof VmCellObject) {
				return new VmCellBoolean(left == right);
			/* ----- ARRAY COMPARISON ----- */
			}else if(operator == VaInst.InstBinOperation.OP_SETIF_EQUALS
					&& (left instanceof VmCellArray || left instanceof VmCellArrUByte)
					&& (right instanceof VmCellArray || right instanceof VmCellArrUByte)) {
				return new VmCellBoolean(left == right);
			}else{
				throw new VmError("Invalid binary operator: " + operator);
			}
			
			final VaDatatype lefttype = currentFrame.getRegister(leftreg - 1);
			final VaDatatype righttype = currentFrame.getRegister(rightreg - 1);
			if(lefttype instanceof VaDatatype.DtPrimitive
					&& righttype instanceof VaDatatype.DtPrimitive) {
				final VaDatatype.DtPrimitive leftprim
					= (VaDatatype.DtPrimitive)lefttype;
				final VaDatatype.DtPrimitive rightprim
					= (VaDatatype.DtPrimitive)righttype;
				if((operator == VaInst.InstBinOperation.OP_ARITH_ADD
						|| operator == VaInst.InstBinOperation.OP_ARITH_SUB
						|| operator == VaInst.InstBinOperation.OP_ARITH_MUL
						|| operator == VaInst.InstBinOperation.OP_ARITH_SHL)
						&& leftprim.identity == VaDatatype.DtPrimitive.PRIM_UBYTE
						&& rightprim.identity == VaDatatype.DtPrimitive.PRIM_UBYTE)
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFL);
				if((operator == VaInst.InstBinOperation.OP_ARITH_ADD
						|| operator == VaInst.InstBinOperation.OP_ARITH_SUB
						|| operator == VaInst.InstBinOperation.OP_ARITH_MUL
						|| operator == VaInst.InstBinOperation.OP_ARITH_SHL)
						&& leftprim.identity == VaDatatype.DtPrimitive.PRIM_USHORT
						&& rightprim.identity == VaDatatype.DtPrimitive.PRIM_USHORT)
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFFFL);
				if((operator == VaInst.InstBinOperation.OP_ARITH_ADD
						|| operator == VaInst.InstBinOperation.OP_ARITH_SUB
						|| operator == VaInst.InstBinOperation.OP_ARITH_MUL
						|| operator == VaInst.InstBinOperation.OP_ARITH_SHL)
						&& leftprim.identity == VaDatatype.DtPrimitive.PRIM_UINT
						&& rightprim.identity == VaDatatype.DtPrimitive.PRIM_UINT)
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFFFFFFFL);
			}
			
			return intermediate;
		}
		
		private final VmCell doUnOperation(int operator, int operandreg)
				throws VmError {
			final VmCell operand = getRegister(operandreg);
			
			final VmCell intermediate;
			if(operator == VaInst.InstUnOperation.OP_ARITH_NOT
					&& operand instanceof VmCellBoolean) {
				return new VmCellBoolean(!operand.valueAsBoolean());
			}else if(operator == VaInst.InstUnOperation.OP_ARITH_NOT
					&& operand instanceof VmCellUInt) {
				intermediate = new VmCellUInt(~operand.valueAsUInt());
			}else if(operator == VaInst.InstUnOperation.OP_ARR_SIZE
						&& (operand instanceof VmCellArray
								|| operand instanceof VmCellArrUByte)) {
				intermediate = new VmCellUInt(operand.arraySize());
			}else{
				throw new VmError("Invalid unary operator: " + operator);
			}
			
			final VaDatatype operandtype = currentFrame.getRegister(operandreg - 1);
			if(operandtype instanceof VaDatatype.DtPrimitive) {
				final VaDatatype.DtPrimitive operandprim
					= (VaDatatype.DtPrimitive)operandtype;
				if((operator == VaInst.InstUnOperation.OP_ARITH_NOT)
						&& operandprim.identity == VaDatatype.DtPrimitive.PRIM_UBYTE) {
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFL);
				}else if((operator == VaInst.InstUnOperation.OP_ARITH_NOT)
						&& operandprim.identity == VaDatatype.DtPrimitive.PRIM_USHORT) {
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFFFL);
				}else if((operator == VaInst.InstUnOperation.OP_ARITH_NOT)
						&& operandprim.identity == VaDatatype.DtPrimitive.PRIM_UINT) {
					return new VmCellUInt(intermediate.valueAsUInt() & 0xFFFFFFFFL);
				}
			}
			
			return intermediate;
		}
		
		// returns the frame at "number"
		private final VaFrame getFrame(final int number) {
			return procedure.getFrame(number);
		}
		
		// initializes a frame
		private final void initFrame(final VaFrame frame) {
			currentFrame = frame;
			currentIp = 0;
			stackSize = 0;
			currentRegisters = new VmCell[frame.getRegisterCnt()];
		}
		
		// pushs one virtual register onto the register stack
		private final void pushRegister(final VmCell register) {
			currentRegisters[stackSize] = register;
			stackSize++;
		}
		private final void setRegister(final int number,
				final VmCell register) {
			currentRegisters[number - 1] = register;
			if(number > stackSize)
				stackSize = number;
		}
		
		// returns the register at "number"
		private final VmCell getRegister(final int number) {
			return currentRegisters[number - 1];
		}
		
		// returns true if "supercls" is a superclass of "clsinfo"
		// or if "supercls" equals "clsinfo"
		private final boolean isSuperclass(VaClsInfo clsinfo, VaClsInfo supercls) {
			if(clsinfo == null)
				return false;
			
			if(clsinfo.compatible(supercls))
				return true;
			
			// check parent classes of this class
			final VaClass clsbody = machine.getCls(clsinfo);	
			return isSuperclass(clsbody.parentInfo, supercls);
		}
		
		private final VmCell createArray(final VaDatatype type, final long size) {
			final VaDatatype.DtArray array = (VaDatatype.DtArray)type;
			
			// process special cases to improve performance
			if(array.basetype instanceof VaDatatype.DtPrimitive) {
				final VaDatatype.DtPrimitive baseprim
						= (VaDatatype.DtPrimitive)array.basetype;
				if(baseprim.identity == VaDatatype.DtPrimitive.PRIM_UBYTE)
					return new VmCellArrUByte(size);
			}
			
			// create a generic array
			return new VmCellArray(array, size);
		}
	}
	
	// virtual machine that owns this code description
	private final VmMachine machine;
	
	// procedure associated with this description
	private final VaProcedure procedure;
	
	private final VaMthInfo mthInfo;
	
	public VmVirtualCode(VmMachine machine, VaMthInfo mthinfo,
			VaProcedure procedure) {
		this.machine = machine;
		this.mthInfo = mthinfo;
		this.procedure = procedure;
	}
	
	@Override public VaMthInfo getMthInfo() {
		return mthInfo;
	}
	@Override public final VmExecutor invoke() {
		return new VirtualExecutor(procedure);
	}
}
